package com.ruyuan.user.server.domain.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.ruyuan.common.model.Features;
import com.ruyuan.common.model.PageDTO;
import com.ruyuan.user.api.model.enums.AuthResourceTypeEnum;
import com.ruyuan.user.server.domain.converter.AuthorityConverter;
import com.ruyuan.user.server.domain.model.AuthResource;
import com.ruyuan.user.server.domain.model.AuthResourceFeaturesKey;
import com.ruyuan.user.server.domain.model.Authority;
import com.ruyuan.user.server.domain.service.AuthorityDomainService;
import com.ruyuan.user.server.domain.service.request.PageQueryAuthResourceReq;
import com.ruyuan.user.server.domain.strategy.AuthorityGenerateStrategy;
import com.ruyuan.user.server.domain.strategy.AuthorityGenerateStrategyFactory;
import com.ruyuan.user.server.repository.bean.AuthResourceDO;
import com.ruyuan.user.server.repository.bean.AuthResourceExample;
import com.ruyuan.user.server.repository.bean.AuthorityDO;
import com.ruyuan.user.server.repository.bean.AuthorityExample;
import com.ruyuan.user.server.repository.mapper.AuthResourceMapper;
import com.ruyuan.user.server.repository.mapper.AuthorityMapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xx
 */
@Service
public class AuthorityDomainServiceImpl implements AuthorityDomainService {

    @Autowired
    private AuthorityConverter authorityConverter;

    @Autowired
    private AuthResourceMapper authResourceMapper;

    @Autowired
    private AuthorityMapper authorityMapper;

    @Override
    public Long saveAuthResource(AuthResource authResource) {
        if (authResource == null) {
            return null;
        }
        //1.资源保存
        AuthResourceDO authResourceDO = authorityConverter.convertToAuthResourceDO(authResource);
        if (authResourceDO.getId() != null && authResourceDO.getId() > 0L) {
            authResourceMapper.updateByPrimaryKeyWithBLOBs(authResourceDO);
        } else {
            authResourceMapper.insertSelective(authResourceDO);
        }
        authResource.setId(authResourceDO.getId());
        //2.生成资源对应的权限
        AuthorityGenerateStrategy authorityGenerateStrategy = AuthorityGenerateStrategyFactory.buildStrategy(authResource.getType());
        if (authorityGenerateStrategy != null) {
            List<Authority> existAuthorities = queryAuthorityByResource(authResource.getId());
            //数据库已有的权限code
            Set<String> existAuthorityCode = existAuthorities.stream().map(Authority::getCode).collect(Collectors.toSet());
            //生成当前资源对应的权限
            List<Authority> authorities = authorityGenerateStrategy.generateAuthorities(authResource);
            Map<String, Authority> newAuthorityMap = authorities.stream().collect(Collectors.toMap(Authority::getCode, authority -> authority));
            //删除多余的权限 插入新增的权限 更新已有的权限
            // 2.1.删除多余的权限
            existAuthorities.stream().filter(existAuthority -> !newAuthorityMap.containsKey(existAuthority.getCode()))
                    .map(Authority::getId).forEach(authorityMapper::deleteByPrimaryKey);
            // 2.2.插入新增的权限
            authorities.stream().filter(newAuthority -> !existAuthorityCode.contains(newAuthority.getCode()))
                    .map(authorityConverter::convertAuthorityToDO).forEach(authorityMapper::insertSelective);
            // 2.3.更新已有的权限
            existAuthorities.stream().filter(existAuthority -> newAuthorityMap.containsKey(existAuthority.getCode())).forEach(existAuthority -> {
                Authority newAuthority = newAuthorityMap.get(existAuthority.getCode());
                newAuthority.setId(existAuthority.getId());
                authorityMapper.updateByPrimaryKey(authorityConverter.convertAuthorityToDO(newAuthority));
            });
        }
        //3.生成操作资源对应的api资源，例如一个button按钮它后面隐含着一个api访问请求/api/user/xxx
        if (authResource.getType().getCode().equals(AuthResourceTypeEnum.OPERATION.getCode())) {
            generateApiResource(authResource);
        }

        return authResourceDO.getId();
    }

    @Override
    public List<Authority> queryAuthorityByResource(Long resourceId) {
        AuthorityExample authorityExample = new AuthorityExample();
        authorityExample.createCriteria().andAuthResourceIdEqualTo(resourceId).andIsDeletedEqualTo("n");
        List<AuthorityDO> authorityDOS = authorityMapper.selectByExampleWithBLOBs(authorityExample);
        if (CollectionUtils.isNotEmpty(authorityDOS)) {
            return authorityDOS.stream().map(authorityConverter::convertAuthorityDOToModel).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    private void generateApiResource(AuthResource authResource) {
        String urls = authResource.getFeatures().getFeature(AuthResourceFeaturesKey.URL);
        if (StringUtils.isNotBlank(urls)) {
            Arrays.stream(urls.split(",")).forEach(url -> {
                String apiResourceCode = url.replace("/", "_").toUpperCase();
                //是否已经存在相同的资源
                AuthResourceExample authResourceExample = new AuthResourceExample();
                authResourceExample.createCriteria().andCodeEqualTo(apiResourceCode)
                        .andTypeEqualTo(AuthResourceTypeEnum.API.getCode())
                        .andIsDeletedEqualTo("n").andOrgIdEqualTo(authResource.getOrgId());
                List<AuthResourceDO> existApiResourceList = authResourceMapper.selectByExampleWithBLOBs(authResourceExample);
                if (CollectionUtils.isNotEmpty(existApiResourceList)) {
                    //更新操作
                    for (AuthResourceDO existApiResource : existApiResourceList) {
                        Features newFeatures;
                        if (StringUtils.isNotBlank(existApiResource.getFeatures())) {
                            newFeatures = Features.of(existApiResource.getFeatures());
                        } else {
                            newFeatures = Features.of();
                        }
                        //features中会保存它关联的功能资源id
                        String relatedFunctionIds = newFeatures.getFeature(AuthResourceFeaturesKey.RELATED_FUNCTION_RESOURCE_IDS);
                        if (StringUtils.isNotBlank(relatedFunctionIds)) {
                            List<String> existIds = Arrays.asList(relatedFunctionIds.split(","));
                            if (existIds.contains(String.valueOf(authResource.getId()))) {
                                continue;
                            }
                            relatedFunctionIds += "," + authResource.getId();
                        } else {
                            relatedFunctionIds = authResource.getId().toString();
                        }
                        newFeatures.addFeature(AuthResourceFeaturesKey.RELATED_FUNCTION_RESOURCE_IDS, relatedFunctionIds);
                        existApiResource.setFeatures(newFeatures.toString());
                        authResourceMapper.updateByPrimaryKeyWithBLOBs(existApiResource);
                    }
                } else {
                    //如果不存在，则创建新的api资源
                    AuthResource apiAuthResource = new AuthResource();
                    apiAuthResource.setName(apiResourceCode);
                    apiAuthResource.setType(AuthResourceTypeEnum.API);
                    apiAuthResource.setCode(apiResourceCode);
                    apiAuthResource.setOrgId(authResource.getOrgId());
                    apiAuthResource.setDescription(authResource.getDescription());
                    apiAuthResource.setModifier(authResource.getModifier());
                    apiAuthResource.setDeleted(false);
                    apiAuthResource.setFeatures(Features.of());
                    apiAuthResource.getFeatures().addFeature(AuthResourceFeaturesKey.RELATED_FUNCTION_RESOURCE_IDS, authResource.getId().toString());
                    apiAuthResource.getFeatures().addFeature(AuthResourceFeaturesKey.URL, url);
                    saveAuthResource(apiAuthResource);
                }
            });
        }
    }

    @Override
    public PageDTO<AuthResource> queryAuthResourceByCondition(PageQueryAuthResourceReq req) {
        AuthResourceExample example = new AuthResourceExample();
        // 最新插入的资源放在最上面
        example.setOrderByClause(" id desc");
        AuthResourceExample.Criteria criteria = example.createCriteria();
        criteria.andOrgIdEqualTo(req.getOrgId()).andIsDeletedEqualTo("n");
        Optional.ofNullable(req.getAuthResourceCode()).ifPresent(criteria::andCodeEqualTo);
        if (CollectionUtils.isNotEmpty(req.getAuthResourceTypes())) {
            criteria.andTypeIn(req.getAuthResourceTypes().stream().map(AuthResourceTypeEnum::getCode).collect(Collectors.toList()));
        }
        PageHelper.startPage(req.getCurrent(), req.getPageSize());
        Page<AuthResourceDO> authResourceDOPage = (Page<AuthResourceDO>) authResourceMapper.selectByExampleWithBLOBs(example);
        if (authResourceDOPage == null || CollectionUtils.isEmpty(authResourceDOPage.getResult())) {
            return PageDTO.emptyPage(req.getPageSize());
        }
        PageDTO<AuthResource> pageResult = new PageDTO<>();
        pageResult.setData(authResourceDOPage.getResult().stream().map(authorityConverter::convertAuthResourceDOToModel).collect(Collectors.toList()));
        pageResult.setCurrent(authResourceDOPage.getPageNum());
        pageResult.setPageSize(authResourceDOPage.getPageSize());
        pageResult.setCurPageSize(authResourceDOPage.size());
        pageResult.setTotal(authResourceDOPage.getTotal());
        pageResult.setPages(authResourceDOPage.getPages());
        return pageResult;
    }

    @Override
    public void delAuthResource(Long resourceId) {
        authResourceMapper.deleteByPrimaryKey(resourceId);
        AuthorityExample authorityExample = new AuthorityExample();
        authorityExample.createCriteria().andAuthResourceIdEqualTo(resourceId);
        authorityMapper.deleteByExample(authorityExample);
    }

    @Override
    public List<Authority> queryAuthorityByIds(List<Long> authorityIds) {
        AuthorityExample authorityExample = new AuthorityExample();
        authorityExample.createCriteria().andIdIn(authorityIds);
        List<AuthorityDO> authorityDOList = authorityMapper.selectByExampleWithBLOBs(authorityExample);
        if (CollectionUtils.isEmpty(authorityDOList)) {
            return new ArrayList<>();
        }
        return authorityDOList.stream().map(authorityConverter::convertAuthorityDOToModel).collect(Collectors.toList());
    }

    @Override
    public List<AuthResource> queryResourcesByIds(List<Long> resourceIds) {
        AuthResourceExample authResourceExample = new AuthResourceExample();
        authResourceExample.createCriteria().andIdIn(resourceIds);
        List<AuthResourceDO> authResourceDOList = authResourceMapper.selectByExampleWithBLOBs(authResourceExample);
        if (CollectionUtils.isEmpty(authResourceDOList)) {
            return new ArrayList<>();
        }
        return authResourceDOList.stream().map(authorityConverter::convertAuthResourceDOToModel).collect(Collectors.toList());
    }
}
